﻿#Const ALLOW_EMPTY_FILES = True
'#Const _4GIG_PACKAGE_LIMITATION = False
#Const DEBUG_NOTCOMPRESS = False
#Const DEBUG_NOTENCRYPT = False

Namespace Compilers
    Friend Class X1020_Compiler
        Inherits GenericCompiler
        ' Second Stable Compiler Release intended for General Production
        ' Modified version of DDC 1.0.1.0_R

        ' DEBUG CHANGELOG
        ' Updated to support MFT Encryption using Symmetric keys
        ' Modified _MFT_MD5_OFFSET and _MFT_OFFSET to improve data scrambling


        ' This protocol development has been started on 01/04/2013



        Private Structure _InternalCompilationConstantsCollection
            ' The compilation process supports dynamic buffer sizes that adapts to the hardware and system resources
            ' depending on its workload. When a whole cycle is completed almost instantly, that means that the system can handle
            ' a little more push without difficulty, so the buffer will be increased.
            ' When that cycle takes too much time (beyond an internally defined barrier) that means that the system is under
            ' heavy workload, the buffer needs to be reduced to increase application responsiveness.
            ' Time is measured using Ticks, each Tick last an specific ammount of time, usually in seconds, and defined by TickLength
            ' Lets say that the cycle takes X ticks, and the defined barrier is Y ticks.
            ' If X > Y then the buffer is too big
            ' IF X < (Y - (Y * 25 / 100)) then the buffer can be increased
            ' And optimal buffer is between 75%-99% of the barrier limit
            ' TickBarrier is Y, and represents that limit for the Buffer size, everything bigger than it will be considered bad

            Public TickLength As TimeSpan
            Public TickBarrier As Integer
            Public BufferVariant As Long ' How much the buffer will vary between tick adjustments
            Public BufferTopLimit As Long ' How much the buffer will be allowed to grow to

            Public Sub Init()
                ' Each tick longs 200ms and will be allowed 10 ticks only (2 seconds)
                TickLength = New TimeSpan(0, 0, 0, 0, 200)
                TickBarrier = 10
                BufferVariant = 1024
                BufferTopLimit = 64 * 1024 * 1024 ' 64 MiB
            End Sub
        End Structure
        Private _InternalConstants As _InternalCompilationConstantsCollection

        Private Const _MFT_MD5_OFFSET As Long = 5
        Private Const _MFT_OFFSET As Long = 8
        Private Const _MFT_OBFUSCATIONSEED_FLAGNAME As String = "ObfuscationSeed"
        Private Const _MFT_ENCRYPTIONPASSWORD_FLAGNAME As String = "EncryptionPassword"

        Private _MFT_ENCRYPTION_SALT() As Byte = {&HC9, &H65, &H42, &H2B, &H99, &HDC, &H81, &HDA, &H26, &HBC, &H19, &H8A, &H9C, &HEE, &H68, &H8} ' Randomnly Generated  DONT CHANGE NEVER

        Public Overrides ReadOnly Property CompilerName As String
            Get
                Return "X1020 Cryptographic Release Compiler (X1020SRC) V. 1.0.2.0_D"
            End Get
        End Property

        Public Overrides ReadOnly Property CompilerProtocol As String
            Get
                Return "1.0.2.0_D"
            End Get
        End Property

        Private CompilationResultTable As Commons.PackageCompilationResult
        Private SimulationResultTable As Commons.PackageCompilerSimulationResult



        ' Constructor
        Public Sub New()
            CompilationResultTable = New Commons.PackageCompilationResult
            SimulationResultTable = New Commons.PackageCompilerSimulationResult
            _InternalConstants.Init()
        End Sub



        ' Simulate Operation
        Public Overrides Function Simulate() As Commons.PackageCompilerSimulationResult
            AddLogC("System Latency: " & GetFormattedTime(CalculateLatencyTime))
            AddLogC("Starting compile simulation")

            Dim reducedOutput As Boolean = False ' When true, a lot of files were specified, reduce the output to avoid vertical scrolling like a crazy!
            Dim staticTrigger As Boolean = True

            Dim output As Commons.PackageCompilerSimulationResult = New Commons.PackageCompilerSimulationResult

            If Configuration.CurrentConfiguration.InputFilename Is Nothing Then
                AddLogC("No source files found.")
                output.Code = PackageCompilerSimulationResultCode.NoSourceFiles
                Return output
            End If
            If Configuration.CurrentConfiguration.OutputFilename = "" Then
                AddLogC("No output file specified.")
                output.Code = PackageCompilerSimulationResultCode.NoTargetFile
                Return output
            End If

            ' Check sourcefiles
            Dim totalSize As ULong = 0
            Try
                AddLogC("Checking " & Configuration.CurrentConfiguration.InputFilename.Count & " sourcefiles...")
                Dim i As Integer
                If Configuration.CurrentConfiguration.InputFilename.Count > 32 Then
                    AddLogC("Info: A high number of files will be processed")
                    AddLogC("      To avoid trashing the console window, the output level will be reduced.")
                    reducedOutput = True
                End If
                For i = 0 To Configuration.CurrentConfiguration.InputFilename.Count - 1
                    Dim fi As New IO.FileInfo(Configuration.CurrentConfiguration.InputFilename(i))
                    If fi.Exists = False Then
                        AddLogC("Source file: <" & i & "> <" & Configuration.CurrentConfiguration.InputFilename(i) & "> not found.")
                        output.Code = PackageCompilerSimulationResultCode.ioCantOpenSourceFile
                        output.Message = Configuration.CurrentConfiguration.InputFilename(i)
                        Return output
                    End If
                    If fi.Length = 0 Then
#If ALLOW_EMPTY_FILES Then
                        GoTo EmptyFileDescriptor
#Else
                    AddLogC("Source file: <" & i & "> <" & Configuration.CurrentConfiguration.InputFilename(i) & "> is empty.")
                    output.Code = PackageCompilerSimulationResultCode.ioSourceFileEmpty
                    output.Message = Configuration.CurrentConfiguration.InputFilename(i)
                    Return output
#End If

                    End If
                    ' Try to open file
                    Dim fs As New IO.FileStream(fi.FullName, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.ReadWrite, 8, False)
                    If fs.CanRead = False Then
                        AddLogC("Source file: <" & i & "> <" & Configuration.CurrentConfiguration.InputFilename(i) & "> is not readable.")
                        output.Code = PackageCompilerSimulationResultCode.ioCantOpenSourceFile
                        output.Message = Configuration.CurrentConfiguration.InputFilename(i)
                        Return output
                    End If
                    totalSize += fs.Length
                    'fs.Close()
                    fs.Dispose()
                    fs = Nothing

#If ALLOW_EMPTY_FILES Then
EmptyFileDescriptor:
#End If
                    If reducedOutput = True Then
                        If staticTrigger = False Then
                            ClearLog(True)
                        Else
                            staticTrigger = False
                        End If
                    End If

                    AddLogC("File [" & i + 1 & "/" & Configuration.CurrentConfiguration.InputFilename.Count & "] Checked successfully")
                Next
                AddLogC("Source file check complete")
            Catch ex As Exception
                AddLogC("EXCEPTION: Source file check failed.")
                AddLogC(" Ex: " & ex.Message)
                output.Code = PackageCompilerSimulationResultCode.CompilerException
                output.Message = "Source file check failed. " & ex.Message
                Return output
            End Try

            ' Check target file and drive freespace
            Try
                Dim fi As New IO.FileInfo(Configuration.CurrentConfiguration.OutputFilename)
                If fi.Exists = True Then
                    If Not fi.Length = 0 Then
                        AddLogC("WARNING: TARGET PACKAGE ALREADY EXISTS. WILL BE OVERWRITTED")
                        System.Threading.Thread.Sleep(5000)
                    End If
                End If
                Dim drive As New IO.DriveInfo(Left(fi.FullName, 1))
                AddLogC("Package will be stored inside drive " & UCase(drive.Name) & "  <" & drive.VolumeLabel & ">")
                fi = Nothing
                Dim headerAproximatedSize As ULong = 47 + 256 ' Header + standard size Descriptor Table
                headerAproximatedSize += (16 + 64 + 16) * Configuration.CurrentConfiguration.InputFilename.Count ' Multiply 16 bytes from MD5 + 64 bytes of filename + 16 bytes (8+8) from offsets for every file
                headerAproximatedSize += 128 ' Junk data in the MFT
                headerAproximatedSize += Configuration.CurrentConfiguration.PackageDescription.Length ' Package description

                AddLogC("Header aprox. Size: " & Commons.FormatSizeAsHumanReadable(headerAproximatedSize))
                AddLogC("Datablock aprox. Size: " & Commons.FormatSizeAsHumanReadable(totalSize))
                AddLogC("Drive free space: " & Commons.FormatSizeAsHumanReadable(drive.AvailableFreeSpace))
                If Not (headerAproximatedSize + totalSize) < drive.AvailableFreeSpace Then
                    AddLogC("Not enough space on drive to store the new package.")
                    output.Code = PackageCompilerSimulationResultCode.InsuficientFreeSpace
                    output.Message = headerAproximatedSize + totalSize
                    drive = Nothing
                    Return output
                End If
                drive = Nothing
                AddLogC()
                AddLogC("Target Package size will be (aprox.): " & Commons.FormatSizeAsHumanReadable(headerAproximatedSize + totalSize))
            Catch ex As Exception
                AddLogC("EXCEPTION: Target file check failed.")
                AddLogC(" Ex: " & ex.Message)
                output.Code = PackageCompilerSimulationResultCode.CompilerException
                output.Message = "Target file check failed. " & ex.Message
                Return output
            End Try

            ' Check package flags
            If Configuration.HasFlag(_MFT_OBFUSCATIONSEED_FLAGNAME) Then
                AddLogC("Using manually specified Obfuscation seed <" & Configuration.GetFlag(_MFT_OBFUSCATIONSEED_FLAGNAME, "") & ">")
            Else
                AddLogC("No Obfuscation seed defined. A new one will be created randomly when compiling.")
            End If
            If Configuration.HasFlag(_MFT_ENCRYPTIONPASSWORD_FLAGNAME) Then
                Dim password As String = Configuration.GetFlag(_MFT_ENCRYPTIONPASSWORD_FLAGNAME, "")

                If password.Length > 3 Then
                    AddLogC("Using password <" & password & ">")
                Else
                    AddLogC("Error! Password is too short for encryption.")
                    output.Code = PackageCompilerSimulationResultCode.InvalidPackageFlags
                    output.Message = "Encryption Password too short."
                    Return output
                End If
            Else
                AddLogC("Error! No password defined for Encryption.")
                AddLogC(" Use <flag " + _MFT_ENCRYPTIONPASSWORD_FLAGNAME + "=...> to define one")
                output.Code = PackageCompilerSimulationResultCode.InvalidPackageFlags
                output.Message = "No Encryption Password."
                Return output
            End If

            AddLogC()
            AddLogC("Simulation succeeded")

            output.Code = PackageCompilerSimulationResultCode.SimulationSucceeded
            output.Message = ""
            Return output
        End Function

        ' Compile Operation
        Public Overrides Function Compile() As Commons.PackageCompilationResult
            AddLogC("System Latency: " & GetFormattedTime(CalculateLatencyTime))
            AddLogC("Starting package compilation process")
            Dim startTime As Date = Now
            Dim endTime As Date

            Dim reducedOutput As Boolean = False ' When true, a lot of files were specified, reduce the output to avoid vertical scrolling like a crazy!
            Dim staticTrigger As Boolean = True

            Dim output As Commons.PackageCompilationResult = New Commons.PackageCompilationResult

            ' Package Section containers
            Dim packageHeaderBinary() As Byte ' Package Static Header
            Dim packageDescriptorBinary() As Byte ' Package Descriptor Section
            Dim packageMftBinary() As Byte ' Package MFT Section
            Dim packageDatablockSectionSize As ULong ' Stores the size of the Datablock section
            Dim packageMftHash() As Byte ' Stores the hash of the uncompressed MFT

            If Configuration.CurrentConfiguration.InputFilename Is Nothing Then
                AddLogC("No source files found.")
                output.Code = Commons.PackageCompilationResultCode.NoSourceFiles
                Return output
            End If
            If Configuration.CurrentConfiguration.OutputFilename = "" Then
                AddLogC("No output file specified.")
                output.Code = Commons.PackageCompilationResultCode.NoTargetFile
                Return output
            End If

            ' Check MFT Obfuscation Seed
            If Configuration.HasFlag(_MFT_OBFUSCATIONSEED_FLAGNAME) Then
                AddLogC("Using custom obfuscation seed <" & Configuration.GetFlag(_MFT_OBFUSCATIONSEED_FLAGNAME, "") & ">")
            Else
                ' Generate a new Seed
                Randomize()
                Dim seed As String = ""
                Dim r As New Random()
                Dim seedsize As Byte = r.Next(8, 17)
                Do Until seed.Length >= seedsize
                    seed = seed & Hex(r.Next(16))
                Loop

                Configuration.SetFlag(_MFT_OBFUSCATIONSEED_FLAGNAME, seed)

                AddLogC("Using Automatically generated obfuscation seed <" & seed & ">")
            End If

            ' Open destination package
            Dim ioStream As IO.FileStream = Nothing
            Dim ioWritter As IO.BinaryWriter = Nothing
            Try
                AddLogC("Creating target package...")
                ioStream = New IO.FileStream(Configuration.CurrentConfiguration.OutputFilename, IO.FileMode.Create, IO.FileAccess.ReadWrite, IO.FileShare.None)
                ioWritter = New IO.BinaryWriter(ioStream)
            Catch ex As Exception
                AddLogC("Package creation failed at System IO layer.")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.ioCantOpenDestinationFile
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Pre-Build Package Header
            Try
                Dim sectionStartTime As Date = Now
                AddLogC("Prebuilding package header...")
                Dim buffer() As Byte
                buffer = Commons.ArphoonStaticSignature ' Insert Static Signature
                Commons.InsertIntoArray(buffer, {&H9, &H4}) ' Insert Cert Code
                Commons.InsertIntoArray(buffer, {&HFF, &HFF, &HFF, &HFF}) ' Insert Reserved Area 1
                Commons.InsertIntoArray(buffer, {&H1, &H0, &H2, &H0, &HD}) ' Insert Compiler version
                Commons.InsertIntoArray(buffer, {&HFF, &HFF, &HFF, &HFF}) ' Insert Reserved Area 2
                Commons.InsertIntoArray(buffer, {&H0, &H0, &H0, &H0}) ' Insert a dummy Descriptor table offset
                Commons.InsertIntoArray(buffer, {&H0, &H0, &H0, &H0}) ' Insert a dummy Descriptor size
                Commons.InsertIntoArray(buffer, {&HFF, &HFF, &HFF, &HFF}) ' Insert Reserved Area 3
                Commons.InsertIntoArray(buffer, {&HFF, &HFF, &HFF}) ' Insert Reserved Area 4
#If DEBUG_NOTCOMPRESS Then
                Commons.InsertIntoArray(buffer, Byte.Parse(&H80, System.Globalization.CultureInfo.InvariantCulture)) ' Insert Descriptor Jump flags (80:JumpInDebugMode)
#Else
                Commons.InsertIntoArray(buffer, Byte.Parse(&H82, System.Globalization.CultureInfo.InvariantCulture)) ' Insert Descriptor Jump flags (80:JumpInDebugMode + 02:DecompressWithDeflate)
#End If


                If Not buffer.Length = 47 Then
                    AddLogC("Invalid header size. Aborted")
                    output.Code = PackageCompilationResultCode.InvalidPackageFlags
                    output.Message = "Internal Logic Error"
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End If

                Dim obfuscationSize As Integer = Fix((64 - 4) * Rnd() + 4) ' Calculate a random number between 4 and 64
                Dim obfuscationBumperA(obfuscationSize) As Byte ' Create a random size buffer
                Dim i As Integer
                For i = 0 To obfuscationBumperA.Length - 1
                    Dim byteValue As Byte = Fix((255 - 1) * Rnd() + 1)
                    obfuscationBumperA(i) = byteValue
                Next

                Commons.InsertIntoArray(buffer, obfuscationBumperA) ' Insert an Obfuscation Bumper. This will be a variant length junk section, that its only purpose is to move ahead the datablock section a variable ammount of bytes

                If Not buffer.Length = 47 + obfuscationBumperA.Length Then
                    AddLogC("Invalid header size. Aborted")
                    output.Code = PackageCompilationResultCode.InvalidPackageFlags
                    output.Message = "Internal Logic Error"
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End If


                ' Set package header on memory
                packageHeaderBinary = buffer

                Try
                    Dim sectionEndTime As Date = Now
                    Dim diffTime As TimeSpan = sectionEndTime - sectionStartTime
                    AddLog("STC: " & Commons.GetFormattedTime(diffTime))
                Catch ex As Exception
                    AddLogC("WARNING: STC failed. This will not affect the resulting package")
                End Try
            Catch ex As Exception
                AddLogC("Internal Exception while building package header")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Prebuild Descriptor Section
            Dim packageDescriptorBuilder As System.Text.StringBuilder
            Try
                Dim buffer As New System.Text.StringBuilder(4096 * 4) ' 16.384   16KiB

                AddLogC("Building MFT metadata...")

                ' Insert Descriptor table header
                InsertArpsCode(buffer, "#", "Package Descriptor Table")
                InsertArpsCode(buffer, "#", "Compiled by " & CompilerName)
                InsertArpsCode(buffer, "#", "Using " & CompilerProtocol)
                InsertArpsCode(buffer, "#", "Utility: " & My.Application.Info.ProductName)
                InsertArpsCode(buffer, "#", "Version: " & My.Application.Info.Version.ToString)
                InsertArpsCode(buffer, "#", "Owner: " & My.Application.Info.CompanyName)
                InsertArpsCode(buffer, "#", My.Application.Info.Copyright)
                InsertArpsCode(buffer, "#", My.Computer.Info.OSPlatform & "(" & My.Computer.Info.OSFullName & ")")
                InsertArpsCode(buffer)

                ' OK, we got...
                '  - Header and its Size
                '  - The location where the descriptor table will reside (just put all sizes together)
                ' We still need...
                '  - Compressed Descriptor table and its size
                '  - Update on header: Descriptor offset, size, and MFT MD5
                '  - Compressed MFT, its size and relative location
                '  - Datablock size and its location

                Dim localArch As Integer
                'Undefined = 0
                'UnixGeneric = 1
                'WindowsGeneric = 2
                'Win32 = 3
                'Win64 = 4
                'Unix32 = 5
                'Unix64 = 6
                'Virtualized32 = 7
                'Virtualized64 = 8
                'Mobile = 9
                Select Case My.Computer.Info.OSPlatform
                    Case "Win32NT"
                        localArch = 3 ' Win32
                    Case "Win32S"
                        localArch = 9 ' Mobile
                    Case "Win32Windows"
                        localArch = 2 ' WindowsGeneric
                    Case "WinCE"
                        localArch = 3
                    Case Else
                        localArch = 0 ' Undefined
                End Select

#If DEBUG_NOTCOMPRESS Then
                InsertArpsCode(buffer, "mft_flags", &H80) ' Insert MFT Jump Flags (80=DebugMode)
#Else
                InsertArpsCode(buffer, "mft_flags", &H82) ' Insert MFT Jump Flags (80=DebugMode + 02=DecompressAsDeflate)
#End If

                InsertArpsCode(buffer)
                InsertArpsCode(buffer, "compiler", CompilerName) ' Insert compiler name
                InsertArpsCode(buffer, "compiler_version", My.Application.Info.Version.ToString) ' Insert compiler version
                InsertArpsCode(buffer, "compiler_arch", localArch) ' Insert OS platform
                InsertArpsCode(buffer, "compiler_owner", My.Application.Info.CompanyName) ' Insert compiler owner
                InsertArpsCode(buffer, "compiler_id", "KDERAZORBACK.ARPS.STATIC.X1020") ' Insert compiler static ID
                InsertArpsCode(buffer, "compile_time", Now.ToString(System.Globalization.CultureInfo.InvariantCulture)) ' Insert compilation time
                InsertArpsCode(buffer)
                InsertArpsCode(buffer, "mft_offset", _MFT_OFFSET) ' Insert the offset location where the offset of the MFT is stored, relative to the MD5 Offset
                InsertArpsCode(buffer, "mft_size", ":MFTSIZE_MFTSIZE;") ' Insert the size of the MFT
                InsertArpsCode(buffer, "md5offset", _MFT_MD5_OFFSET) ' Insert the location offset where the MFT MD5 will reside
#If Not DEBUG_NOTENCRYPT Then
                InsertArpsCode(buffer, "obfuscationseed", Configuration.GetFlag(_MFT_OBFUSCATIONSEED_FLAGNAME, "dummy")) ' Insert the obfuscation seed for pre-encrypting MFT table
#End If
                ' Descriptor End
                ' _MFT_MD5_OFFSET bytes
                ' MFT MD5 16 bytes
                ' _MFT_OFFSET bytes
                ' MFT Start

                InsertArpsCode(buffer)
                InsertArpsCode(buffer, "quit", "0") ' Insert a Quit command

                packageDescriptorBuilder = buffer
            Catch ex As Exception
                AddLogC("Internal Exception while prebuilding package descriptor")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try


            ' Build MFT Section
            Try
                Dim sectionStartTime As Date = Now
                Dim buffer As New System.Text.StringBuilder(4096 * 16) ' 65.536.64 KiB
                '   ^ This used to be a Simple String. DA0001: System.String.Concat(.*) = 37.56; Consider using StringBuilder to concatenate Strings

                AddLogC("Caching source files metadata...")

                ' Begin parsing each source file
                Dim i As Long
                Dim myPath As String = IO.Directory.GetCurrentDirectory
                Dim filePositioner As ULong = 0
                If Configuration.CurrentConfiguration.InputFilename.Count > 32 Then
                    AddLogC("Info: A high number of files will be processed")
                    AddLogC("      To avoid trashing the console window,")
                    AddLogC("      the output level will be reduced.")
                    reducedOutput = True
                End If
                AddLog()

                For i = 0 To Configuration.CurrentConfiguration.InputFilename.Count - 1
                    ClearLog(True)
                    AddLog(i + 1 & "/" & Configuration.CurrentConfiguration.InputFilename.Count)

                    With Configuration.CurrentConfiguration
                        Dim line As String = ""
                        Dim filename As String = ""
                        Dim size As ULong
#If ALLOW_EMPTY_FILES Then
                        Dim fileEmpty As Boolean = False
#End If
                        ' Get filename and size
                        Try
                            Dim fi As New IO.FileInfo(.InputFilename(i)) ' Open a FileInfo instance
                            If fi.Exists = False Then
                                AddLogC("Now i want to blow something!")
                                AddLogC("A source file dont exists.")
                                AddLogC("REMEMBER:")
                                AddLogC(" Run a simulation every time you can before compiling")
                                AddLogC(" in order to avoid embarassing situations like this one.")
                                AddLogC(" **nudge**")
                                AddLogC("  file:" & .InputFilename(i))
                                output.Code = PackageCompilationResultCode.ioCantOpenSourceFile
                                output.Message = .InputFilename(i)
                                CloseStreams(ioStream, ioWritter)
                                Return output
                            End If
                            If fi.Length = 0 Then
#If ALLOW_EMPTY_FILES Then
                                fileEmpty = True
#Else
                                AddLogC("Now i want to blow up something!")
                                AddLogC("A source file is empty.")
                                AddLogC("REMEMBER:")
                                AddLogC(" Run a simulation every time you can before compiling")
                                AddLogC(" in order to avoid embarassing situations like this one.")
                                AddLogC(" **nudge**")
                                AddLogC("  file:" & .InputFilename(i))
                                output.Code = PackageCompilationResultCode.ioSourceFileEmpty
                                output.Message = .InputFilename(i)
                                CloseStreams(ioStream, ioWritter)
                                Return output
#End If

                            End If

                            size = fi.Length

                            ' File exists and its not empty
                            If LCase(fi.DirectoryName) = LCase(myPath) Then
                                ' Relative path
                                filename = fi.Name
                            Else
                                ' Absolute path - Try to remove myPath from it and any drive letter
                                If LCase(fi.DirectoryName) Like LCase(myPath & "*") Then
                                    ' Subfolder. Remove myPath from it
                                    filename = fi.DirectoryName
                                    filename = Right(filename, filename.Length - myPath.Length) ' Remove myPath
                                    If Left(filename, 1) = "\" Then filename = Right(filename, filename.Length - 1) ' Remove any leading '\'
                                Else
                                    ' Not a Subfolder. Remove drive letter
                                    filename = fi.DirectoryName
                                    If filename Like "?:*" Then filename = Right(filename, filename.Length - 2)
                                    If Left(filename, 1) = "\" Then filename = Right(filename, filename.Length - 1) ' Remove any leading '\'
                                End If
                                filename &= "\" & fi.Name
                            End If

                            fi = Nothing
                        Catch ex As Exception
                            AddLogC("Now i want to blow something!")
                            AddLogC("Invalid filename specified")
                            AddLogC("REMEMBER:")
                            AddLogC(" Run a simulation every time you can before compiling")
                            AddLogC(" in order to avoid embarassing situations like this one.")
                            AddLogC(" **nudge**")
                            AddLogC("  file:" & .InputFilename(i))
                            output.Code = PackageCompilationResultCode.ioCantOpenSourceFile
                            output.Message = .InputFilename(i)
                            CloseStreams(ioStream, ioWritter)
                            Return output
                        End Try

                        If Configuration.CurrentConfiguration.ShadowDeleteEntries IsNot Nothing AndAlso Configuration.CurrentConfiguration.ShadowDeleteEntries.Count > 0 Then
                            Dim si As Long
                            Dim newList As New List(Of String)()
                            For si = 0 To Configuration.CurrentConfiguration.ShadowDeleteEntries.Count - 1
                                If Not filename.ToLowerInvariant() = Configuration.CurrentConfiguration.ShadowDeleteEntries(si).ToLowerInvariant() Then
                                    newList.Add(Configuration.CurrentConfiguration.ShadowDeleteEntries(si))
                                Else
                                    AddLogC("Warning - A shadow ""deletion"" entry with the same name as an stored file has been found. Removing ""deletion"" entry")
                                    AddLogC(" file: " & filename)
                                End If
                            Next

                            Configuration.CurrentConfiguration.ShadowDeleteEntries = newList
                        End If

                        ' Filename is OK and size too
                        Dim bhash() As Byte
                        Dim hash As String = ""
#If ALLOW_EMPTY_FILES Then
                        If fileEmpty Then GoTo SkipMd5CheckOnEmptyFile
#End If
                        Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider
                        ClearLog(True)
                        AddLog(i + 1 & "/" & Configuration.CurrentConfiguration.InputFilename.Count & " Hashing...")
                        Try
                            Dim fs As New IO.FileStream(.InputFilename(i), IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
                            bhash = md5.ComputeHash(fs)
                            'fs.Close()
                            fs.Dispose()
                            fs = Nothing
                            md5 = Nothing
                            Dim x As Integer
                            For x = 0 To bhash.Length - 1
                                Dim opcode As String
                                opcode = Hex(bhash(x))
                                If opcode.Length = 1 Then opcode = "0" & opcode
                                hash &= opcode
                            Next
                        Catch ex As Exception
                            AddLogC("WARNING: File <" & .InputFilename(i) & "> hash calculation failed")
                            hash = ""
                        End Try

                        If Not hash = "" Then hash = Chr(4) & hash

#If ALLOW_EMPTY_FILES Then
SkipMd5CheckOnEmptyFile:
#End If

                        line = filename & Chr(4) & filePositioner & Chr(4) & size & hash
                        InsertArpsCode(buffer, "file", line)

                        filePositioner += size
                    End With
                Next

                ' Store Deletion entries
                If Configuration.CurrentConfiguration.ShadowDeleteEntries IsNot Nothing AndAlso Configuration.CurrentConfiguration.ShadowDeleteEntries.Count > 0 Then
                    Dim si As Long
                    For si = 0 To Configuration.CurrentConfiguration.ShadowDeleteEntries.Count - 1
                        Dim dname As String = Configuration.CurrentConfiguration.ShadowDeleteEntries(si)
                        dname = dname.Replace("/", "\") ' Replace "/" with "\"
                        If dname(0) = "\" Then dname = dname.Substring(1) ' Remove any leading "\"
                        If dname(dname.Length - 1) = "\" Then dname = dname.Substring(0, dname.Length - 1) ' Remove any trailing "\"
                        Dim dline As String = dname & Chr(4) & filePositioner & Chr(4) & "0" & Chr(4) & "delete"
                        InsertArpsCode(buffer, "file", dline)
                    Next

                    AddLogC(Configuration.CurrentConfiguration.ShadowDeleteEntries.Count & " deletion entries stored.")
                End If

                packageDatablockSectionSize = filePositioner
                packageMftBinary = System.Text.Encoding.ASCII.GetBytes(buffer.ToString)

                ' Compress MFT
                Try
#If DEBUG_NOTCOMPRESS Then
                    Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider
                    Dim rawHash() As Byte = md5.ComputeHash(packageMftBinary)
                    md5 = Nothing
                    packageMftHash = rawHash
                    Exit Try
#Else
                    Dim memBuffer As New IO.MemoryStream()
                    Dim compressor As New System.IO.Compression.DeflateStream(memBuffer, IO.Compression.CompressionMode.Compress, True)
                    ' Calculate the uncompressed md5 now
                    Dim md5 As New System.Security.Cryptography.MD5CryptoServiceProvider
                    Dim rawHash() As Byte = md5.ComputeHash(packageMftBinary)
                    md5 = Nothing
                    packageMftHash = rawHash
                    ' Continue compressing
                    compressor.Write(packageMftBinary, 0, packageMftBinary.Length)
                    'compressor.Flush() ' Useless
                    compressor.Close()
                    Dim outBuffer(memBuffer.Length - 1) As Byte
                    memBuffer.Position = 0
                    memBuffer.Read(outBuffer, 0, memBuffer.Length)
                    memBuffer.Close()
                    compressor.Dispose()
                    compressor = Nothing
                    memBuffer.Dispose()
                    memBuffer = Nothing

                    packageMftBinary = outBuffer
#End If
                Catch ex As Exception
                    AddLogC("Internal Exception while building package MFT.c")
                    AddLogC(" EX: " & ex.Message)
                    output.Code = PackageCompilationResultCode.CompilerException
                    output.Message = ex.Message
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End Try

                ' Obfuscate MFT if seed is defined
                Try
#If DEBUG_NOTENCRYPT Then
                    AddLogC("MFT Obfuscation is dissabled at compiler level.")
#Else
                    Dim seed As String = Configuration.GetFlag(_MFT_OBFUSCATIONSEED_FLAGNAME, "dummy")

                    If seed.Length > 0 Then
                        AddLogC("Obfuscating MFT...")
                        AddLog("0 %")
                        Dim previousProgress As Byte = 100
                        Dim currentProgress As Byte = 0
                        Dim i1 As ULong
                        Dim i2 As Integer = 0

                        For i1 = 0 To packageMftBinary.Length - 1
                            If i2 >= seed.Length Then i2 = 0

                            packageMftBinary(i1) = (packageMftBinary(i1)) Xor (Val(seed(i2)))

                            If i1 > 0 Then
                                currentProgress = Fix((i1 * 100) / packageMftBinary.Length - 1)
                            Else
                                currentProgress = 0
                            End If

                            If currentProgress <> previousProgress Then
                                ClearLog(True)
                                AddLogC(currentProgress & " %")
                                previousProgress = currentProgress
                            End If

                            i2 = i2 + 1
                        Next
                    Else
                        AddLogC("Skipping MFT Obfuscation because manually specified seed is empty.")
                    End If
#End If
                Catch ex As Exception
                    AddLogC("Internal Exception while obfuscating package MFT")
                    AddLogC(" EX: " & ex.Message)
                    output.Code = PackageCompilationResultCode.CompilerException
                    output.Message = ex.Message
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End Try

                ' Encrypt MFT
                Try
#If Not DEBUG_NOTENCRYPT Then
                    AddLogC("Encrypting MFT...")
                    ' Derive key and IV from SALT and password
                    AddLogV("Deriving encryption key and IV from SALT and password...")
                    Dim key() As Byte
                    Dim vector() As Byte
                    Dim password As String = Configuration.GetFlag(_MFT_ENCRYPTIONPASSWORD_FLAGNAME, "1234")
                    AddLogV("Using password <" & password & ">")
                    Dim deriver As New System.Security.Cryptography.Rfc2898DeriveBytes(password, _MFT_ENCRYPTION_SALT, 2740)
                    key = deriver.GetBytes(32)
                    vector = deriver.GetBytes(16)
                    AddLogV(" Encryption Key: " & GetHexDump(key))
                    AddLogV(" Initialization Vector: " & GetHexDump(vector))
                    AddLogV("Creating encryptor...")
                    Dim encryptor As New System.Security.Cryptography.RijndaelManaged()
                    encryptor.Key = key
                    encryptor.IV = vector
                    encryptor.BlockSize = 128
                    Dim encryptedMftStream As New System.IO.MemoryStream()
                    Dim cryptoS As New System.Security.Cryptography.CryptoStream(encryptedMftStream, encryptor.CreateEncryptor, Security.Cryptography.CryptoStreamMode.Write)

                    AddLogV("Encrypting...")

                    Dim currentProgress As Byte = 0
                    Dim previousProgress As Byte = 100
                    Dim i1 As ULong

                    AddLogC("0 %")
                    For i1 = 0 To packageMftBinary.Length - 1
                        cryptoS.WriteByte(packageMftBinary(i1))

                        If i1 > 0 Then
                            currentProgress = Fix((i1 * 100) / packageMftBinary.Length - 1)
                        Else
                            currentProgress = 0
                        End If


                        If currentProgress <> previousProgress Then
                            ClearLog(True)
                            AddLogC(currentProgress & " %")
                            previousProgress = currentProgress
                        End If
                    Next

                    AddLogV("Closing encryptor...")
                    cryptoS.Close()
                    packageMftBinary = encryptedMftStream.ToArray()
                    encryptedMftStream.Dispose()
                    deriver = Nothing
                    encryptedMftStream = Nothing
                    cryptoS = Nothing
#End If
                Catch ex As Exception
                    AddLogC("Internal Exception while encrypting package MFT.e")
                    AddLogC(" EX: " & ex.Message)
                    output.Code = PackageCompilationResultCode.CompilerException
                    output.Message = ex.Message
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End Try

                AddLogC("Package MFT built successfully!")
                AddLogC("ME@= " & Hex(packageMftBinary.Length))
                AddLogC("DB@= " & Hex(packageDatablockSectionSize))

                Try
                    Dim sectionEndTime As Date = Now
                    Dim diffTime As TimeSpan = sectionEndTime - sectionStartTime
                    AddLog("STC: " & Commons.GetFormattedTime(diffTime))
                Catch ex As Exception
                    AddLogC("WARNING: STC failed. This dont affect the resulting package")
                End Try
            Catch ex As Exception
                AddLogC("Internal Exception while building package MFT")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Update Descriptor Table with MFT data
            Dim packageDescriptorString As String = packageDescriptorBuilder.ToString()
            Try
                Dim sectionStartTime As Date = Now
                AddLogC("Updating MFT metadata...")
                packageDescriptorString = Replace(packageDescriptorString, ":MFTSIZE_MFTSIZE;", packageMftBinary.Length)
                packageDescriptorBuilder = Nothing

                ' Compress the Descriptor table
                Try
#If DEBUG_NOTCOMPRESS Then
                    packageDescriptorBinary = System.Text.Encoding.ASCII.GetBytes(packageDescriptorString)
                    Exit Try
#Else
                    Dim memBuffer As New IO.MemoryStream()
                    Dim compressor As New System.IO.Compression.DeflateStream(memBuffer, IO.Compression.CompressionMode.Compress, True)
                    Dim rawBuffer() As Byte = System.Text.Encoding.ASCII.GetBytes(packageDescriptorString)
                    compressor.Write(rawBuffer, 0, rawBuffer.Length)
                    'compressor.Flush() ' Useless
                    compressor.Close()
                    Dim outBuffer(memBuffer.Length - 1) As Byte
                    memBuffer.Position = 0
                    memBuffer.Read(outBuffer, 0, memBuffer.Length)
                    memBuffer.Close()
                    compressor.Dispose()
                    compressor = Nothing
                    memBuffer.Dispose()
                    memBuffer = Nothing

                    packageDescriptorBinary = outBuffer
#End If
                Catch ex As Exception
                    AddLogC("Internal Exception while building package Descriptor.c")
                    AddLogC(" EX: " & ex.Message)
                    output.Code = PackageCompilationResultCode.CompilerException
                    output.Message = ex.Message
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End Try

                AddLogC("Package Descriptor built successfully!")
                AddLogC("ME@= " & Hex(packageDescriptorBinary.Length))

                Try
                    Dim sectionEndTime As Date = Now
                    Dim diffTime As TimeSpan = sectionEndTime - sectionStartTime
                    AddLog("STC: " & Commons.GetFormattedTime(diffTime))
                Catch ex As Exception
                    AddLogC("WARNING: STC failed. This dont affect the resulting package")
                End Try
            Catch ex As Exception
                AddLogC("Internal Exception while updating package Descriptor.c")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Update Header with the final offsets
            Try
                Dim sectionStartTime As Date = Now

                AddLogC("Updating bootstrapper loader...")

                ' Ok, lets update the Header offsets
                ' Descriptor table offset is located at 0x20 (dec 32) in Candy format
                ' Descriptor table size is located at 0x24 (dec 36) in Candy format

                Dim address() As Byte = Commons.GetCandyArrayFromUnsignedLong(packageHeaderBinary.Length)
                packageHeaderBinary(31) = address(0)
                packageHeaderBinary(32) = address(1)
                packageHeaderBinary(33) = address(2)
                packageHeaderBinary(34) = address(3) ' Update Descriptor Offset

                address = Commons.GetCandyArrayFromUnsignedLong(packageDescriptorBinary.Length)
                packageHeaderBinary(35) = address(0)
                packageHeaderBinary(36) = address(1)
                packageHeaderBinary(37) = address(2)
                packageHeaderBinary(38) = address(3) ' Update Descriptor Size

                Try
                    Dim sectionEndTime As Date = Now
                    Dim diffTime As TimeSpan = sectionEndTime - sectionStartTime
                    AddLog("STC: " & Commons.GetFormattedTime(diffTime))
                Catch ex As Exception
                    AddLogC("WARNING: STC failed. This dont affect the resulting package")
                End Try
            Catch ex As Exception
                AddLogC("Internal Exception while updating package loader")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' OK, last section
            ' For now the package (on-memory) looks as follows:
            ' [HEADER]
            ' [OBFUSCATION BUMPER] (innexistent)
            ' [DESCRIPTOR]
            ' [OBFUSCATION BUMPER] (innexistent)
            ' [MFT MD5]
            ' [OBFUSCATION BUMPER] (innexistent)
            ' [MFT]
            ' [DATABLOCK] (innexistent)

            ' Dump header
            Try
                AddLogC("Writting cached buffers [1/3]...")
                ioWritter.Write(packageHeaderBinary)
            Catch ex As Exception
                AddLogC("Internal Exception while dumping buffers")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Dump Descriptor Table
            Try
                AddLogC("Writting cached buffers [2/3]...")
                ioWritter.Write(packageDescriptorBinary)
                ' Dump first Obfuscation bumper
                ' Make sure this values are the same as those defined in the Descriptor Table offsets
                For i = 1 To _MFT_MD5_OFFSET ' _MFT_MD5_OFFSET Bytes
                    ioWritter.Write(New Byte() {Fix(((255 - 1) * Rnd() + 1))})
                Next
            Catch ex As Exception
                AddLogC("Internal Exception while dumping buffers")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Dump MFT Table
            Try
                AddLogC("Writting cached buffers [3/3]...")

                ' Dump MFT MD5
                ioWritter.Write(packageMftHash)

                ' Dump second Obfuscation bumper
                ' Make sure this values are the same as those defined in the Descriptor Table offsets
                For i = 1 To _MFT_OFFSET ' _MFT_OFFSET Bytes
                    ioWritter.Write(New Byte() {Fix(((255 - 1) * Rnd() + 1))})
                Next

                ' Dump MFT
                ioWritter.Write(packageMftBinary)
            Catch ex As Exception
                AddLogC("Internal Exception while dumping buffers")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            Dim outOfMemoryErrorCount As ULong = 0

            ' Dump each file
            Dim statistics As New PackageCompilationStatistics
            Try
                Dim sectionStartTime As Date = Now

                AddLog("Packing " & Configuration.CurrentConfiguration.InputFilename.Count & " files...")

                Dim i As Long
                Dim x As String
                Dim bufferSize As Long = 4096
                Dim lowerBarrier As Long
                Dim averageBuffer(19) As Long
                Dim averageBufferCounter As Byte
                Dim averageTickCounter As Byte
                Dim averageTicks(19) As Long
                Dim maxBufferSize As Long = bufferSize
                Dim minBufferSize As Long = bufferSize
                Dim maxTicks As Long = 1
                Dim minTicks As Long = 99999999999
                lowerBarrier = _InternalConstants.TickLength.Ticks * _InternalConstants.TickBarrier
                lowerBarrier = lowerBarrier - Fix(lowerBarrier * 25 / 100)
                For i = 0 To Configuration.CurrentConfiguration.InputFilename.Count - 1
                    x = Configuration.CurrentConfiguration.InputFilename(i)
                    If reducedOutput = True Then
                        If staticTrigger = True Then
                            staticTrigger = False
                        Else
                            ClearLog(True)
                        End If
                    End If
                    Dim component() As String = Split(x, "\")
                    If Not component Is Nothing Then
                        If Not component.Length = 0 Then
                            AddLog(i & ": " & component(component.Length - 1))
                        End If
                    End If
                    Erase component
                    'AddLog()
                    Dim fs As New IO.FileStream(x, IO.FileMode.Open, IO.FileAccess.Read, IO.FileShare.Read)
                    Dim br As New IO.BinaryReader(fs)
                    Dim ignoredCycles As Byte = 0
                    Dim ignoredCyclesFirstTrigger As Boolean = True
                    Do Until (fs.Position >= fs.Length) ' Or (fs.CanRead = False)
                        If ignoredCycles > 64 Then
                            If ignoredCyclesFirstTrigger = True Then
                                ignoredCyclesFirstTrigger = False
                                AddLog()
                            End If
                            ignoredCycles = 0
                            ClearLog(True)
                            AddLog(Commons.FormatSizeAsHumanReadable(fs.Position) & " / " & Commons.FormatSizeAsHumanReadable(fs.Length) & "  buffering at " & Commons.FormatSizeAsHumanReadable(bufferSize), False, False)
                        Else
                            ignoredCycles += 1
                        End If

                        Dim cycleStart As Date = Now ' Stores the beginning of a cycle
CycleStart:
                        Try
                            Dim buffer() As Byte
                            buffer = br.ReadBytes(bufferSize)

                            If buffer Is Nothing Then
                                AddLogC("Internal Exception while packing data")
                                AddLogC(" Cant read source file " & fs.Name)
                                output.Code = PackageCompilationResultCode.ioCantOpenSourceFile
                                output.Message = fs.Name
                                CloseStreams(ioStream, ioWritter)
                                br.Close()
                                br = Nothing
                                fs.Dispose()
                                fs = Nothing
                                Return output
                            End If
                            ioWritter.Write(buffer)
                            ioWritter.Flush()
                            Erase buffer
                        Catch ex As System.OutOfMemoryException
                            outOfMemoryErrorCount += 1

                            ' Buffer too big!
                            If (bufferSize - _InternalConstants.BufferVariant <= 0) Then
                                AddLogC("ERROR: = Out of Memory =")
                                AddLogC(" Hit " & outOfMemoryErrorCount & IIf(outOfMemoryErrorCount = 1, " time", " times"))
                                AddLogC("  The compiler ran out of resources")
                                AddLogC(" Package is NOT complete. Compilation failed")
                                output.Code = PackageCompilationResultCode.CompilerException
                                output.Message = "Out of Memory"
                                CloseStreams(ioStream, ioWritter)
                                br.Close()
                                br = Nothing
                                fs.Dispose()
                                fs = Nothing
                                Return output
                            End If

                            bufferSize -= _InternalConstants.BufferVariant
                            _InternalConstants.BufferTopLimit = bufferSize

                            GoTo CycleStart
                        Catch ex As Exception
                            AddLogC("Internal Exception while packing data")
                            AddLogC(" Cant read source file " & fs.Name)
                            AddLogC(" Package is NOT complete. Compilation failed")
                            AddLogC(" EX: " & ex.Message)
                            output.Code = PackageCompilationResultCode.ioCantOpenSourceFile
                            output.Message = fs.Name
                            CloseStreams(ioStream, ioWritter)
                            br.Close()
                            br = Nothing
                            fs.Dispose()
                            fs = Nothing
                            Return output
                        End Try
                        Dim cycleEnd As Date = Now ' Stores the end of a cycle
                        Dim took As TimeSpan = cycleEnd - cycleStart
                        If took.Ticks > (_InternalConstants.TickLength.Ticks * _InternalConstants.TickBarrier) Then
                            ' Buffer too big!
                            If Not bufferSize = _InternalConstants.BufferVariant Then
                                If bufferSize - _InternalConstants.BufferVariant < 1024 Then
                                    bufferSize = 1024
                                Else
                                    bufferSize -= _InternalConstants.BufferVariant
                                End If
                            End If

                            If bufferSize < minBufferSize Then minBufferSize = bufferSize
                        Else
                            If (took.Ticks < lowerBarrier) AndAlso (bufferSize < _InternalConstants.BufferTopLimit) Then
                                ' Buffer too small!
                                bufferSize += _InternalConstants.BufferVariant
                            End If

                            If bufferSize > maxBufferSize Then maxBufferSize = bufferSize
                        End If

                        averageBufferCounter += 1
                        averageTickCounter += 1

                        If averageBufferCounter > 19 Then averageBufferCounter = 0
                        If averageTickCounter > 19 Then averageTickCounter = 0

                        averageBuffer(averageBufferCounter) = bufferSize
                        averageTicks(averageTickCounter) = took.Ticks

                        If took.Ticks < minTicks Then minTicks = took.Ticks
                        If took.Ticks > maxTicks Then maxTicks = took.Ticks
                    Loop
                    br.Close()
                    br = Nothing
                    fs.Close()
                    fs.Dispose()
                    fs = Nothing
                    If ignoredCyclesFirstTrigger = True Then AddLog()
                    ClearLog(True)
                    If reducedOutput = False Then AddLog("OK !")
                Next

                ' Update Statistics
                statistics.BufferSteeping = _InternalConstants.BufferVariant
                statistics.StartTime = startTime
                statistics.MaxBufferUsed = maxBufferSize
                statistics.MinBufferUsed = minBufferSize
                statistics.MaxTicksPerIO = maxTicks
                statistics.MinTicksPerIO = minTicks
                statistics.TickBarrier = _InternalConstants.TickBarrier
                statistics.TickLength = _InternalConstants.TickLength.Ticks
                statistics.PackedFiles = Configuration.CurrentConfiguration.InputFilename.Count

                Dim sum As Long = 0
                Dim items As Byte = 0
                For Each xi As Long In averageBuffer
                    If Not xi = 0 Then
                        sum += xi
                        items += 1
                    End If
                Next
                If (Not sum = 0) And (Not items = 0) Then
                    statistics.AverageBufferUsed = sum / items
                End If
                sum = 0
                items = 0
                For Each xi As Long In averageTicks
                    If Not xi = 0 Then
                        sum += xi
                        items += 1
                    End If
                Next
                If (Not sum = 0) And (Not items = 0) Then
                    statistics.AverageTicksPerIO = sum / items
                End If
                statistics.PackedBytes = packageDatablockSectionSize

                Try
                    Dim sectionEndTime As Date = Now
                    Dim diffTime As TimeSpan = sectionEndTime - sectionStartTime
                    AddLog("STC: " & Commons.GetFormattedTime(diffTime))
                Catch ex As Exception
                    AddLogC("WARNING: STC failed. This dont affect the resulting package")
                End Try
            Catch ex As Exception
                AddLogC("Internal Exception while packing files")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Update statistics
            Try
                statistics.OutputSize = ioStream.Position
                If Not ioStream.Position = (packageHeaderBinary.Length + packageDatablockSectionSize + packageMftBinary.Length + packageDescriptorBinary.Length + _MFT_MD5_OFFSET + _MFT_OFFSET + 16) Then
                    AddLogC("CRITICAL FAILURE ===")
                    AddLogC(" PACKED DATA DONT MATCH EXPECTED ONE.")
                    AddLogC(" THE SOURCE FILES HAVE CHANGED DURING THE COMPILATION PROCESS")
                    AddLogC(" ABORTED.")
                    output.Code = PackageCompilationResultCode.CompilationAborted
                    output.Message = "Source files changed"
                    CloseStreams(ioStream, ioWritter)
                    Return output
                End If
            Catch ex As Exception
                AddLogC("Internal Exception while preparing to close file")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Close package
            Try
                AddLogC("Closing file...")
                AddLogV("Out of Memory Exceptions avoided " & outOfMemoryErrorCount & IIf(outOfMemoryErrorCount = 1, " time", " times"))
                CloseStreams(ioStream, ioWritter)
            Catch ex As Exception
                AddLogC("Internal Exception while closing file")
                AddLogC(" EX: " & ex.Message)
                output.Code = PackageCompilationResultCode.CompilerException
                output.Message = ex.Message
                CloseStreams(ioStream, ioWritter)
                Return output
            End Try

            ' Calculate TCS and EndTime
            Try
                endTime = Now
                statistics.TcsDiffTime = Commons.GetFormattedTime(endTime - startTime)
                statistics.EndTime = endTime
            Catch ex As Exception
                AddLogC("WARNING: Internal Exception while calculating TCS")
                AddLogC("This dont affect the output package")
                AddLogC(" EX: " & ex.Message)
            End Try

            AddLog()
            AddLogC("Operation completed.")
            output.Code = PackageCompilationResultCode.CompilationSucceeded
            output.Message = "OK"
            output.Statistics = statistics
            Return output
        End Function
    End Class
End Namespace

